﻿/****************************************
 *  copyright (c) 2008 leo shen
 *  
 *  all rights reserved         
 * 
 *  e-mail : syl.lucky@gmail.com
 * *************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using DotUpdater.Core.VersionGeters;
using DotUpdater.Core.Versions;
using DotUpdater.Core.CurrentFilesGeters;
using DotUpdater.Core.LocalFilesAccessors;

namespace DotUpdater.Core
{
    public class Updater
    {
        IVersionGeter m_currentVersionGeter;
        IVersionGeter m_localVersionGeter;
        List<UpdateItem> m_needUpdateItems;
        ICurrentFilesGeter m_currentFilesGeter;
        ILocalFilesAccessor m_localFilesAccessor;

        IEnumerator<UpdateItem> m_currentUpdatingItem;
        int m_currentUpdateItemIndex = 0;

        int getTotalUpdatedPercentage()
        {
            int result = (int)((m_currentUpdateItemIndex / m_needUpdateItems.Count) * 100);
            return result > 100 ? 100 : result;
        }
     
        
        public Updater(IVersionGeter localVersionGeter, IVersionGeter currentVersionGeter, ICurrentFilesGeter currentFilesGeter, ILocalFilesAccessor localFilesGeter)
        {
            m_localVersionGeter = localVersionGeter;
            m_currentVersionGeter = currentVersionGeter;
            m_currentFilesGeter = currentFilesGeter;
            m_localFilesAccessor = localFilesGeter;
        }

        public bool IsNeedUpdate()
        {
            return m_currentVersionGeter.GetVersion().IsLasterThan(m_localVersionGeter.GetVersion());
        }

        public void BeginUpdate()
        {
            m_currentUpdatingItem = m_needUpdateItems.GetEnumerator();
            m_currentFilesGeter.GetFileProgressChanged += new System.Net.DownloadProgressChangedEventHandler(m_currentFilesGeter_GetFileProgressChanged);
            m_currentFilesGeter.GetFileCompleted += new System.Net.DownloadDataCompletedEventHandler(m_currentFilesGeter_GetFileCompleted);

            System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(this.UpdateNext));
            th.Start();
        }        

        void UpdateNext()
        {
            if (m_currentUpdatingItem.MoveNext())
            {
                m_currentUpdateItemIndex += 1;
                App_File fileInfo = m_currentUpdatingItem.Current.FileOrFolder as App_File;
                if (fileInfo != null)
                {
                    if (m_currentUpdatingItem.Current.Operation == UpdateOperation.Add || m_currentUpdatingItem.Current.Operation == UpdateOperation.Replace)
                    {
                        m_currentFilesGeter.GetFileAsyc(fileInfo);
                    }
                    else if (m_currentUpdatingItem.Current.Operation == UpdateOperation.Delete)
                    {
                        OnUpdateProgressChanged(new UpdateProgressChangedEventArgs(m_currentUpdatingItem.Current, 0, getTotalUpdatedPercentage(), "删除"));
                        m_localFilesAccessor.DeleteFile(fileInfo);
                        OnUpdateProgressChanged(new UpdateProgressChangedEventArgs(m_currentUpdatingItem.Current, 100, getTotalUpdatedPercentage(), "完成"));
                        UpdateNext();
                    }
                }
                else
                {
                    App_Folder folderInfo = m_currentUpdatingItem.Current.FileOrFolder as App_Folder;
                    if (folderInfo != null)
                    {
                        if (m_currentUpdatingItem.Current.Operation == UpdateOperation.Add)
                        {
                            OnUpdateProgressChanged(new UpdateProgressChangedEventArgs(m_currentUpdatingItem.Current, 0, getTotalUpdatedPercentage(), "添加"));
                            m_localFilesAccessor.AddFolder(folderInfo);
                            OnUpdateProgressChanged(new UpdateProgressChangedEventArgs(m_currentUpdatingItem.Current, 100, getTotalUpdatedPercentage(), "完成"));
                            UpdateNext();
                        }
                        else if (m_currentUpdatingItem.Current.Operation == UpdateOperation.Delete)
                        {
                            OnUpdateProgressChanged(new UpdateProgressChangedEventArgs(m_currentUpdatingItem.Current, 0, getTotalUpdatedPercentage(), "删除"));
                            m_localFilesAccessor.DeleteFolder(folderInfo);
                            OnUpdateProgressChanged(new UpdateProgressChangedEventArgs(m_currentUpdatingItem.Current, 100, getTotalUpdatedPercentage(), "完成"));
                            UpdateNext();
                        }
                    }
                }
            }
            else
            {
                OnUpdateCompleted(EventArgs.Empty);
            }
        }

        void  m_currentFilesGeter_GetFileProgressChanged(object sender, System.Net.DownloadProgressChangedEventArgs e)
        {
            OnUpdateProgressChanged(new UpdateProgressChangedEventArgs((UpdateItem)this.m_currentUpdatingItem.Current, e.ProgressPercentage, getTotalUpdatedPercentage(), "下载"));
        }

        void m_currentFilesGeter_GetFileCompleted(object sender, System.Net.DownloadDataCompletedEventArgs e)
        {
            OnUpdateProgressChanged(new UpdateProgressChangedEventArgs((UpdateItem)this.m_currentUpdatingItem.Current, 0, getTotalUpdatedPercentage(), "安装"));
            m_localFilesAccessor.ReplaceFile((App_File)((UpdateItem)m_currentUpdatingItem.Current).FileOrFolder, e.Result);
            OnUpdateProgressChanged(new UpdateProgressChangedEventArgs((UpdateItem)this.m_currentUpdatingItem.Current, 100, getTotalUpdatedPercentage(), "完成"));
            UpdateNext();
        }       

        public List<UpdateItem> GetNeedUpdateItems()
        {
            if (m_needUpdateItems == null)
            {
                m_needUpdateItems = new List<UpdateItem>();
                App_Folder curFolder = m_currentVersionGeter.GetAppFolderAndFiles();
                App_Folder localFolder = m_localVersionGeter.GetAppFolderAndFiles();
                GenerateUpdateItems(m_needUpdateItems, curFolder, localFolder);
            }
            return m_needUpdateItems;
        }

        private void GenerateUpdateItems(List<UpdateItem> items, App_Folder curParentFolder, App_Folder localParentFolder)
        {
            foreach (App_Folder curfolder in curParentFolder.SubFolders)
            {
               App_Folder  localFolder = localParentFolder.SubFolders.Find(new Predicate<App_Folder>(delegate (App_Folder folder)
                    {
                        return curfolder.Equals(folder);
                    }));
                if (localFolder == null)
                {
                    items.Add(new UpdateItem(UpdateOperation.Add, curfolder));
                }
                else
                {
                    GenerateUpdateItems(items, curfolder, localFolder);
                }
            }

            foreach (App_Folder localFolder in localParentFolder.SubFolders)
            {
                if (!curParentFolder.SubFolders.Contains(localFolder))
                {
                    items.Add(new UpdateItem(UpdateOperation.Delete, localFolder));
                }
            }

            foreach (App_File curFile in curParentFolder.Files)
            {
                if (curFile.UpdateMode == UpdateMode.never)
                    continue;
                

                App_File localFile = localParentFolder.Files.Find(new Predicate<App_File>(delegate(App_File file)
                {
                    return curFile.Equals(file);
                }));

                if (localFile == null)
                {
                    items.Add(new UpdateItem(UpdateOperation.Add, curFile));
                }
                else
                {
                    if (curFile.Version.IsLasterThan(localFile.Version) || curFile.UpdateMode == UpdateMode.always)
                    {
                        items.Add(new UpdateItem(UpdateOperation.Replace, curFile));
                    }
                }
            }

            foreach (App_File localFile in localParentFolder.Files)
            {
                if (!curParentFolder.Files.Contains(localFile))
                {
                    items.Add(new UpdateItem(UpdateOperation.Delete, localFile));
                }
            }
        }

        #region properties

        public ProgramVersion CurrentVersion
        {
            get { return (ProgramVersion)m_currentVersionGeter.GetVersion(); }
        }

        #endregion

        #region events

        public class UpdateProgressChangedEventArgs : System.EventArgs
        {
            UpdateItem m_updateItem;
            int m_itemProgressPercentage;
            int m_totalProgressPercentage;
            string m_operate;

            public UpdateProgressChangedEventArgs(UpdateItem updateItem, int itemProgressPercentage, int totalProgressPercentage, string operate)
            {
                m_updateItem = updateItem;
                m_itemProgressPercentage = itemProgressPercentage;
                m_totalProgressPercentage = totalProgressPercentage;
                m_operate = operate;
            }

            public UpdateItem UpdateItem
            {
                get { return m_updateItem; }
            }

            public int ItemProgressPercentage
            {
                get { return m_itemProgressPercentage; }
            }

            public int TotalProgressPercentage
            {
                get { return m_totalProgressPercentage; }               
            }

            public string Operate
            {
                get { return m_operate; }
            }
        }

        public delegate void UpdateProgressChangedHandler(object sender, UpdateProgressChangedEventArgs e);

        public event UpdateProgressChangedHandler UpdateProgressChanged;

        protected void OnUpdateProgressChanged(UpdateProgressChangedEventArgs e)
        {
            if (UpdateProgressChanged != null)
            {
                UpdateProgressChanged(this, e);
            }
        }

        public event EventHandler UpdateCompleted;

        protected void OnUpdateCompleted(EventArgs e)
        {
            if (UpdateCompleted != null)
            {
                UpdateCompleted(this, e);
            }
        }

        #endregion
    }

    
}
